#pragma once
#include "KMeansStandardV1_Util.h"
#include "KMeansStandardV1NonConcurrent.h"
#include "OpenClTemplate.h"
#include "Logger.h"

#include <math.h>

namespace Algorithm
{

template<unsigned int OBSERVATION_VECTOR_SIZE, unsigned int NUMBER_OF_OBSERVATIONS, unsigned int NUMBER_OF_CENTROIDS >
class KMeansStandardV1ConcurrentV2
{
public:
	typedef KMeansStandardV1_Util<OBSERVATION_VECTOR_SIZE, NUMBER_OF_OBSERVATIONS, NUMBER_OF_CENTROIDS > SupportedKMeansStandardV1_Util;
	typedef KMeansStandardV1NonConcurrent<OBSERVATION_VECTOR_SIZE, NUMBER_OF_OBSERVATIONS, NUMBER_OF_CENTROIDS > SupportedKMeansStandardV1NonConcurrent;

	static void algorithm(const typename SupportedKMeansStandardV1_Util::TSpAlgorithmData& _algorithmData)
	{
		const unsigned int KERNEL_WORK_ITEM_GLOBAL_WORK_SIZE=32*2*2;
		const unsigned int KERNEL_WORK_ITEM_LOCAL_WORK_SIZE=32*2;
		const unsigned int KERNEL_WORK_ITEM_OBSERVATIONS_PROCESSED=NUMBER_OF_OBSERVATIONS/KERNEL_WORK_ITEM_GLOBAL_WORK_SIZE
			+(NUMBER_OF_OBSERVATIONS%KERNEL_WORK_ITEM_GLOBAL_WORK_SIZE?1:0);
		const unsigned int KERNEL_WORK_ITEM_CENTROIDSS_PROCESSED=NUMBER_OF_CENTROIDS/KERNEL_WORK_ITEM_GLOBAL_WORK_SIZE
			+(NUMBER_OF_CENTROIDS%KERNEL_WORK_ITEM_GLOBAL_WORK_SIZE?1:0);

		std::string buildOptions_;
		{ // create preprocessor defines for the kernel
		  char buf[256]; 
		  sprintf_s(buf,"-D OBSERVATION_VECTOR_SIZE=%d -D NUMBER_OF_OBSERVATIONS=%d -D NUMBER_OF_CENTROIDS=%d -D KERNEL_WORK_ITEM_OBSERVATIONS_PROCESSED=%d -D KERNEL_WORK_ITEM_CENTROIDSS_PROCESSED=%d -D DATA_POINC_VECTOR_ELEMENT_EPSILON=%d",
			  OBSERVATION_VECTOR_SIZE, NUMBER_OF_OBSERVATIONS,NUMBER_OF_CENTROIDS,KERNEL_WORK_ITEM_OBSERVATIONS_PROCESSED,KERNEL_WORK_ITEM_CENTROIDSS_PROCESSED,DATA_POINC_VECTOR_ELEMENT_EPSILON);
		  buildOptions_ += std::string(buf);
		}

		OpenClTemplate::SharedPtr spOpenClTemplate=
			OpenClTemplate::constructDefault("KMeansStandardV1ConcurrentV2.cl",buildOptions_);
		cl::Kernel kernel
			=spOpenClTemplate->buildKernel("kMeans");;

		SupportedKMeansStandardV1_Util::TObservations::TTransposedSharedPtr transposedSharedPtrObservations=_algorithmData->observations.calculateTransposed();
		cl::Buffer bufferObservations_(
			spOpenClTemplate->context, 
            CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,
            sizeof((*transposedSharedPtrObservations).data),
            (*transposedSharedPtrObservations).data);

		SupportedKMeansStandardV1_Util::TCentroids::TTransposedSharedPtr transposedSharedPtrCurrentCentroids=_algorithmData->currentCentroids.calculateTransposed();
        cl::Buffer bufferCurrentCentroids_(
			spOpenClTemplate->context, 
            CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,
            sizeof((*transposedSharedPtrCurrentCentroids).data),
            (*transposedSharedPtrCurrentCentroids).data);

        cl::Buffer bufferClosestCentroid_(
			spOpenClTemplate->context, 
            CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,
            sizeof(_algorithmData->closestCentroid.data),
            _algorithmData->closestCentroid.data);

		kernel.setArg(0, bufferObservations_);
		kernel.setArg(1, bufferCurrentCentroids_);
		kernel.setArg(2, bufferClosestCentroid_);

		cl::Event event;

		const size_t WORK_GROUP_SIZE_=32;

		spOpenClTemplate->queue.enqueueNDRangeKernel
			(
			kernel,
			cl::NullRange,
			cl::NDRange(KERNEL_WORK_ITEM_GLOBAL_WORK_SIZE),//globalThreads
			cl::NDRange(KERNEL_WORK_ITEM_LOCAL_WORK_SIZE),//localThreads
			NULL,
			&event);

		spOpenClTemplate->waitForKernelExecution(event);

		spOpenClTemplate
			->queue.enqueueReadBuffer(
			bufferClosestCentroid_,
			CL_TRUE,
			0,
			sizeof(_algorithmData->closestCentroid.data),
            _algorithmData->closestCentroid.data);
		

	}

	


	static void debugingFunction1()
	{

		std::string buildOptions_;
		{ // create preprocessor defines for the kernel
		  char buf[256]; 
		  sprintf_s(buf,"-D OBSERVATION_VECTOR_SIZE=%d -D NUMBER_OF_OBSERVATIONS=%d -D NUMBER_OF_CENTROIDS=%d",
			  OBSERVATION_VECTOR_SIZE, NUMBER_OF_OBSERVATIONS,NUMBER_OF_CENTROIDS);
		  buildOptions_ += std::string(buf);
		}
		

		OpenClTemplate::SharedPtr spOpenClTemplate=
			OpenClTemplate::constructDefault("KMeansStandardV1ConcurrentV1.cl",buildOptions_);
		cl::Kernel kernelFindClosestCentroid
			=spOpenClTemplate->buildKernel("findClosestCentroid");;


			

		for(int i=0;true;i++)
		{
			Logger::getOStream() << std::endl<<"debugingFunction1 loop " << i;

			SupportedKMeansStandardV1_Util::TSpAlgorithmData inputAlgorithmData
				=SupportedKMeansStandardV1_Util::GenerateAlgorithmDataRandom::generate();
			SupportedKMeansStandardV1_Util::TSpAlgorithmData output1AlgorithmData(new SupportedKMeansStandardV1_Util::TAlgorithmData);
			SupportedKMeansStandardV1_Util::TSpAlgorithmData output2AlgorithmData(new SupportedKMeansStandardV1_Util::TAlgorithmData);
			*output1AlgorithmData=*inputAlgorithmData;
			*output2AlgorithmData=*inputAlgorithmData;

			SupportedKMeansStandardV1NonConcurrent::findClosestCentroid(output1AlgorithmData);
			findClosestCentroid(output2AlgorithmData,spOpenClTemplate,kernelFindClosestCentroid);


			if(!(output1AlgorithmData->closestCentroid==output2AlgorithmData->closestCentroid))
			{
				int i=0;
				i++;
			}

		}

	}


};

}//namespace Algorithm
